# -*- coding: utf-8 -*-
# -输入和输出:print和文件操作,异常 -
# @Time    : 2019/7/9 16:48
# @Author  : hwx
# @Email   : @163.com
# @File    : 官方教程7_8.py
# @Software: PyCharm
import json
import os
import sys

import math


def fun1():
    # 可选的格式说明符可以跟在表达式后面。这样可以更好地控制值的格式化方式。以下示例将pi舍入到小数点后三位
    print(f'The value of pi is approximately {math.pi:.3f}.')
    # 在 ':' 后传递一个整数可以让该字段成为最小字符宽度。这在使列对齐时很有用
    table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
    for name, phone in table.items():
        print(f'{name:10} ==> {phone:10d}')


# fun1()


def fun2():
    """
字符串的 format() 方法
1.花括号和其中的字符（称为格式字段）将替换为传递给 str.format() 方法的对象。花括号中的数字可用来表示传递给 str.format()
方法的对象的位置。
2.如果在 str.format() 方法中使用关键字参数，则使用参数的名称引用它们的值。:
3.位置和关键字参数可以任意组合
    """
    print('We are the {} who say "{}!"'.format('knights', 'Ni'))
    print('{0} and {1}'.format('spam', 'eggs'))
    print('{1} and {0}'.format('spam', 'eggs'))
    # 2
    print('This {food} is {adjective}.'.format(
        food='spam', adjective='absolutely horrible'))
    # 3
    print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
                                                       other='Georg'))


# fun2()


def fun3():
    """
1.如果你有一个非常长的格式字符串，你不想把它拆开，那么你最好按名称而不是位置引用变量来进行格式化。
这可以通过简单地传递字典和使用方括号 '[]' 访问键来完成:
2.这也可以通过使用 '**' 符号将表作为关键字参数传递
    """
    # 1
    table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
    print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
          'Dcab: {0[Dcab]:d}'.format(table))
    # 2
    table2 = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
    print(
        'Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(
            **table2))
    # 3
    for x in range(11, 20):
        print('{0:2d} {1:3d} {2:4d}'.format(x, x * x, x * x * x))
    # 4 str.zfill() ，它会在数字字符串的左边填充零。它能识别正负号:
    print('12'.zfill(5))
    print('-3.15'.zfill(7))
    print('-3.15'.zfill(9))
    print('3.15'.zfill(9))
    print('315'.zfill(9))
    print('-3.15'.zfill(3))
    print('3.141592653595'.zfill(7))


# fun3()


def fun4():
    """
读写文件:
1.open() 返回一个 file object，最常用的有两个参数： open(filename, mode)
2.在处理文件对象时，最好使用 with 关键字。 优点是当子句体结束后文件会正确关闭，即使在某个时刻引发了异常。
3.要读取文件内容，请调用 f.read(size) ，它会读取一些数据并将其作为字符串（在文本模式下）或字节对象（在二进制模式下）
返回。 size 是一个可选的数字参数。当 size 被省略或者为负的时候，将读取并返回文件的整个内容；
4.f.readline() 从文件中读取一行；换行符（\n）留在字符串的末尾，如果文件不以换行符结尾，则在文件的最后一行省略。
这使得返回值明确无误；如果 f.readline() 返回一个空的字符串，则表示已经到达了文件末尾，
而空行使用 '\n' 表示，该字符串只包含一个换行符
5.要从文件中读取行，你可以循环遍历文件对象。这是内存高效，快速的，并简化代码:
6.如果你想以列表的形式读取文件中的所有行，你也可以使用 list(f) 或 f.readlines()。
7.f.write(string) 会把 string 的内容写入到文件中，并返回写入的字符数

    """
    # 1
    f1 = open('log2.txt', 'r')
    f1.close()
    # 2
    with open('log2.txt', 'r', encoding='UTF-8') as f2:       # 注意编码
        readdata = f2.read()
        print(readdata)
        print(f2.closed)    # 没执行完，此时还没关闭文件
    print(f2.closed)    # 执行完with语句块，此时文件是关闭的
    # 5
    f3 = open('log2.txt', 'r')
    for line in f3:
        print(line, end='')    # end=' ' ,end= '!'  符号会出现在开头
    f3.close()
    print('\n', '________________________________________')
    # 7
    f4 = open('log3.txt', 'w')
    print(f4.write('This is a test\n'))
    f4.close()


# fun4()


def fun5():
    """
1.f.tell() 返回一个整数，给出文件对象在文件中的当前位置，表示为二进制模式下时从文件开始的字节数，以及文本模式下的不透明
数字。要改变文件对象的位置，请使用 f.seek(offset, from_what) 。通过向参考点添加 offset 来计算位置；
参考点由 from_what 参数指定。from_what*值为0时，表示从文件开头开始，1 表示从当前位置，2 表示把文件末尾作为参考点。
*from_what 可以省略，默认为0，即使用文件开头作为参考点。
2.../ 表示当前文件所在的目录的上一级目录
./ 表示当前文件所在的目录(可以省略)
/ 表示当前站点的根目录(域名映射的硬盘目录)
3.检查是否是文件及文件是否存在
    """
    f5 = open('log3.txt', 'rb+')
    f5.write(b'0123456789abcdef')
    print(f5.seek(5))
    print(f5.read(1))
    print(f5.seek(-3, 2))
    print(f5.read(1))
    f5.close()
    # 2
    filename = '../text/test.txt'
    with open(filename, 'r') as f:
        ft = f.read()
    print(ft)
    # 3
    print(os.path.exists('../text/hlm.txt'))   # ../text --True     ../text/tt.txt  -False
    print(os.path.isfile('../text/test.txt'))


fun5()


def fun6():
    """
1.Python 允许你使用称为 JSON (JavaScript Object Notation) 的流行数据交换格式，而不是让用户不断的编写和调试代码以将复杂的
数据类型保存到文件中。名为 json 的标准模块可以采用 Python 数据层次结构，并将它们转化为字符串表示形式；
这个过程称为 serializing 。从字符串表示中重建数据称为 deserializing 。在序列化和反序列化之间，表示对象的字符串可能
已存储在文件或数据中，或通过网络连接发送到某个远程机器
2.如果你有一个对象 x ，你可以用一行简单的代码来查看它的 JSON 字符串表示,
dumps() 函数的另一个变体叫做 dump() ，它只是将对象序列化为 text file 。因此，如果 f 是一个 text file 对象，我们可以
这样做:json.dump(x, f),
3.要再次解码对象，如果 f 是一个打开的以供阅读的 text file 对象:x = json.load(f)
    """
    jsondemo = {'name': 'jjo', 'age': '19'}
    # json.dumps([1, 'koio', 33])
    print(json.dumps(jsondemo))


# fun6()

# 错误和异常:1.语法错误又称解析错误，可能是你在学习Python 时最容易遇到的错误,
#            2.异常:即使语句或表达式在语法上是正确的，但在尝试执行时，它仍可能会引发错误。
#            在执行时检测到的错误被称为*异常*，异常不一定会导致严重后果
def fun7():
    """
1.try语句try 语句的工作原理如下。
首先，执行 try 子句 （try 和 except 关键字之间的（多行）语句）。
如果没有异常发生，则跳过 except 子句 并完成 try 语句的执行。
如果在执行try 子句时发生了异常，则跳过该子句中剩下的部分。然后，如果异常的类型和 except 关键字后面的异常匹配，
则执行 except 子句 ，然后继续执行 try 语句之后的代码。
如果发生的异常和 except 子句中指定的异常不匹配，则将其传递到外部的 try 语句中；如果没有找到处理程序，
则它是一个 未处理异常，执行将停止并显示如上所示的消息。
2.如果发生的异常和 except 子句中的类是同一个类或者是它的基类，则异常和except子句中的类是兼容的（但反过来则不成立 ---
列出派生类的except 子句与基类兼容）。例如，下面的代码将依次打印 B, C, D;请注意如果 except 子句被颠倒
（把 except B 放到第一个），它将打印 B，B，B --- 即第一个匹配的 except 子句被触发。
3.

    """
    while True:
        try:
            x = int(input("请输入一个数字"))
            break
        except ValueError:
            print("这不是有效数字，请重试！")
    print("______________________________________________________________________")
#  继承类

    class B(Exception):
        pass

    class C(B):
        pass

    class D(C):
        pass

    for cls in [B, C, D]:
        try:
            raise cls()
        except D:
            print("D")
        except C:
            print("C")
        except B:
            print("B")


# fun7()


def fun8():
    """
1.最后的 except 子句可以省略异常名，以用作通配符。但请谨慎使用，因为以这种方式很容易掩盖真正的编程错误！
它还可用于打印错误消息，然后重新引发异常（同样允许调用者处理异常
2.try ... except 语句有一个可选的 else 子句，在使用时必须放在所有的 except 子句后面。
对于在try 子句不引发异常时必须执行的代码来说很有用
3.except 子句可以在异常名称后面指定一个变量。这个变量和一个异常实例绑定，它的参数存储在 instance.args 中。为了方便起见，
异常实例定义了 __str__() ，因此可以直接打印参数而无需引用 .args 。也可以在抛出之前首先实例化异常，并根据需要向
其添加任何属性。type(Exception) Exception.args
4.如果异常有参数，则它们将作为未处理异常的消息的最后一部分（'详细信息'）打印。
异常处理程序不仅处理 try 子句中遇到的异常，还处理 try 子句中调用（即使是间接地）的函数内部发生的异常
5.finally 子句 总会在离开 try 语句前被执行，无论是否发生了异常。 当在 try 子句中发生了异常且尚未被 except 子句处理
（或者它发生在 except 或 else 子句中）时，它将在 finally 子句执行后被重新抛出。 当
 try 语句的任何其他子句通过 break, continue 或 return 语句离开时，finally 也会在“离开之前”被执行，
6.raise 语句允许程序员强制发生指定的异常,raise 唯一的参数就是要抛出的异常。这个参数必须是一个异常实例或者是一个异常类
（派生自 Exception 的类）。如果传递的是一个异常类，它将通过调用没有参数的构造函数来隐式实例化:raise NameError('HiThere')
raise ValueError  # shorthand for 'raise ValueError()'
    """
    # 2
    # print(sys.argv[:])
    for arg in sys.argv[:]:
        # print(arg)
        try:
            # print(arg)
            f9 = open(arg, 'r', encoding='UTF-8')  # 最好加上encoding='UTF-8'
        except OSError:
            print('cannot open', arg)
        else:
            print(arg, 'has', len(f9.readlines()), 'lines')
            f9.close()
    # 4
    try:
        ioo = 2 / 0
    except Exception as e:    # ZeroDivisionError as e
        print("运行时遇到错误：", e)
    # 5
    try:
        raise KeyboardInterrupt
    finally:
        print('Goodbye, world!')


# fun8()
